tree grammar CalcChecker;

options {
    tokenVocab=Calc;                    // import tokens from Calc.tokens
    ASTLabelType=CommonTree;            // AST nodes are of type CommonTree
}

@header {
package vb.week3.calc;
import java.util.Set;
import java.util.HashSet;
}

// Alter code generation so catch-clauses get replaced with this action. 
// This disables ANTLR error handling: CalcExceptions are propagated upwards.
@rulecatch { 
    catch (RecognitionException e) { 
        throw e; 
    } 
}

@members {
    // idset - a set of declared identifiers.
    private Set<String> idset = new HashSet<String>();   
    
    public boolean  isDeclared(String s)     { return idset.contains(s); }
    public void     declare(String s)        { idset.add(s);             }
}

program
    :   ^(PROGRAM (declaration | statement)+)
    ;
    
declaration
    :   ^(VAR id=IDENTIFIER type)
        {   if (isDeclared($id.text))
                throw new CalcException($id, "is already declared");
            else 
                declare($id.getText()); 
        }
    ;
 
statement 
    :   compound_expr
    |   ^(PRINT compound_expr)
    |   ^(SWAP IDENTIFIER IDENTIFIER)
    |   ^(DO statement+ WHILE compound_expr)
    ;
    
compound_expr
    :   ^(BECOMES id=IDENTIFIER compound_expr)
        {   if (!isDeclared($id.text))
                throw new CalcException($id, "is not declared");
        }
    |   ^(IF compound_expr compound_expr compound_expr)
    |   ^(LESSTHAN compound_expr compound_expr) 
    |   ^(LESSEQUAL compound_expr compound_expr) 
    |   ^(GREATERTHAN compound_expr compound_expr)
    |   ^(GREATEREQUAL compound_expr compound_expr)
    |   ^(EQUAL compound_expr compound_expr)
    |   ^(NOTEQUAL compound_expr compound_expr)
    |   ^(PLUS compound_expr compound_expr)
    |   ^(MINUS compound_expr compound_expr)
    |   ^(TIMES compound_expr operand)
    |   ^(DIVIDEBY compound_expr operand)
    |   operand
    ;
        
operand
    :   id=IDENTIFIER 
        {   if (!isDeclared($id.text))
                throw new CalcException($id, "is not declared");
        }
    |   n=NUMBER
    ;
    
type
    :   INTEGER
    ;
